package au.com.identityconcepts.windowslive.sso.ws;

import java.util.Iterator;  
import java.util.List;  
import java.util.Properties;

import java.io.*;

import org.apache.cxf.io.CachedOutputStream; 
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.interceptor.*;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.*;

import org.jdom.*;
import org.jdom.output.XMLOutputter;
import org.jdom.input.SAXBuilder;
import org.xml.sax.InputSource;
import org.jdom.xpath.*;


public class StreamInterceptorJDOM extends AbstractPhaseInterceptor { 

	private StreamEndingInterceptor ending = new StreamEndingInterceptor(); 
	public static final String ORIGINAL_OUTPUT = "original_output";	
	public static Element element = null;
	
	protected Properties properties;
	private static Properties system = System.getProperties();
	private boolean debug = false;
	private boolean sslDebug = false;

	public StreamInterceptorJDOM() {		
		super(Phase.PRE_STREAM); 
		//addBefore(AttachmentOutInterceptor.class.getName());

		addAfter(AttachmentOutInterceptor.class.getName());
		addBefore(StaxOutInterceptor.class.getName()); 
		
		// Load properties
		try {			
			if (system.getProperty("WLIDConfigFilePath") == null)
				system.setProperty("WLIDConfigFilePath", "c:/WindowsLiveSSO.properties");						
			
			// Retrieve the properties stored in the WindowsLiveSSO.properties file
			properties = new Properties();
		    FileInputStream fis = new FileInputStream(system.getProperty("WLIDConfigFilePath"));
		    properties.load(fis);		
		} catch (Exception ex) {
			System.out.println("Unable to read all properties from client.properties file - StreamInterceptorJDOM");
			ex.printStackTrace();
		}	
		this.debug = Boolean.parseBoolean(this.properties.getProperty("au.com.identityconcepts.windowslive.debug"));
		this.sslDebug = Boolean.parseBoolean(this.properties.getProperty("au.com.identityconcepts.windowslive.sslDebug"));
	} 

	public void handleMessage(Message message) { 
		OutputStream os = message.getContent(OutputStream.class); 
		message.put(ORIGINAL_OUTPUT, os); 
		CachedStream cs = new CachedStream(); 
		message.setContent(OutputStream.class, cs);         

		try { 
			cs.doFlush(); 
		} catch (IOException e) { 
			e.printStackTrace(); 
		} 
		message.getInterceptorChain().add(ending); 				
	}     

	public void handleFault(Message message) { 
	} 

	public class StreamEndingInterceptor extends AbstractPhaseInterceptor { 

		public StreamEndingInterceptor() { 
			super(Phase.PRE_STREAM_ENDING); 
			addAfter(AttachmentOutInterceptor.AttachmentOutEndingInterceptor.class.getName());			
		}

		public void handleMessage(Message message) { 
			try { 
				CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class); 
				OutputStream os = (OutputStream) message.get(ORIGINAL_OUTPUT); 

				StringBuilder sb = new StringBuilder(); 
				csnew.writeCacheTo(sb); 

				String tmpXML = sb.toString(); 
				
				if (debug)
					System.out.println("\nOriginal\n" + tmpXML); 

				// Replace the text node with a CDATA node
				Document document = null;			
				try {					
					SAXBuilder builder = new SAXBuilder(false);						
					
					document = builder.build(new InputSource(new StringReader(tmpXML)));					
					Element root = document.getRootElement();					
					
					XPath xpath = XPath.newInstance("/descendant-or-self::x:ppSoapHeader25");					
					xpath.addNamespace("x","http://schemas.microsoft.com/Passport/SoapServices/CredentialServiceAPI/V1");					
					Element element = (Element) xpath.selectNodes(document).get(0);
					
					// Before
					if (debug) {
						System.out.println("\nBefore removal");
						listChildren(root, 0);
					}
					
					// After					
					List parent = element.getParentElement().getChildren();
					parent.remove(element);					
					if (debug) {
						System.out.println("\nAfter removal");
						listChildren(root, 0);
					}
				   
				    // Add it again as CDATA
				    Element e = new Element("ppSoapHeader25");
				    e.addContent((new CDATA(element.getText())));
				    parent.add(e);				    
				    
				    if (debug) {
				    	System.out.println("\nAfter re-addition");
				    	listChildren(root, 0);
				    }									    				    
				} catch (Exception e) {
					System.out.println(e.toString());
				}
				
				// Push it back to tmpXML and into the OutputStream
				XMLOutputter outputter = new XMLOutputter();
				tmpXML = outputter.outputString(document);																							
		
				InputStream in = new ByteArrayInputStream(tmpXML.getBytes());             
				BufferedOutputStream bos = new BufferedOutputStream(os); 
				CachedOutputStream.copyStream(in, bos, 16384); 
				
				csnew.close(); 
				in.close();				
				bos.close(); 
				os.flush(); 
				 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} catch (Exception e) {
				e.printStackTrace();
			}			
		} 
	} 

	private class CachedStream extends CachedOutputStream { 
		public CachedStream() { 
			super(); 
		} 
		protected void doFlush() throws IOException { 
			currentStream.flush(); 
		} 

		protected void doClose() throws IOException { 
		} 

		protected void onWrite() throws IOException { 
		} 

	} 
	
	public static void listChildren(Element current, int depth) {		   
	    printSpaces(depth);
	    System.out.println(current.getName());
	    List children = current.getChildren();
	    Iterator iterator = children.iterator();
	    while (iterator.hasNext()) {
	      Element child = (Element) iterator.next();
	      listChildren(child, depth+1);
	    }
	    
	  }
	  
	private static void printSpaces(int n) {   
	    for (int i = 0; i < n; i++) {
	      System.out.print(' '); 
	    }	    
	}
} 
